package agi

import (
	"strconv"
	"time"
	"math"
	"unsafe"
	"reflect"
	"fmt"
)

// @see http://studygolang.com/articles/2909
func BytesToStrUnsafe(rawBytes []byte) string {
	return *(*string)(unsafe.Pointer(&rawBytes))
}

// @see http://shinriyo.hateblo.jp/entry/2015/02/19/Go%E8%A8%80%E8%AA%9E%E3%81%AE%E5%B0%8F%E6%95%B0%E7%82%B9%E3%81%AE%E5%9B%9B%E6%8D%A8%E4%BA%94%E5%85%A5
func Round(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return math.Floor(f * shift + .5) / shift
}

func StrToFloat(value string) float64 {
	float, err := strconv.ParseFloat(value, 64)
	if err != nil {
		return 0
	}
	return float
}

// 先尝试转浮点，然后再转整型
func StrToFInt(value string) int {
	float := StrToFloat(value)
	return int(float)
}

func StrToFInt64(value string) int64 {
	float := StrToFloat(value)
	return int64(float)
}

func StrToInt(value string) int {
	number, err := strconv.Atoi(value)
	if err == nil {
		return number
	}
	return 0
}

// 取得整型值所表达的布尔类型
// 不同于golang提供的ParseBool
func StrToIntBool(value string) bool {
	// "" => false
	if len(value) <= 0 {
		return false
	}
	i := StrToFInt(value) // 先尝试尽量取得这个字符串表示的整型值
	if i <= 0 {
		return false
	}
	return true
}

// 将字符串的时间表示，转为时间，注意是类似2014-12-31，不是整型值
// 如果要指定时区解析，直接调用time.ParseInLocation吧
func StrToTime(value string, layout string) time.Time {
	// Mon Jan 2 15:04:05 -0700 MST 2006
	t, err := time.ParseInLocation(layout, value, AppTimeLoc())
	if err != nil {
		return time.Time{}
	}
	return t
	//    fmt.Println(t.Unix(), err)
	//    fmt.Println(time.Unix(t.Unix(), 0).Format("2006-01-02 15:04:05"))
}

func IntToTime(value int64) time.Time {
	if value <= 0 {
		return time.Time{}
	}
	return time.Unix(value, 0)
}

// go的时间
// var t = time.Time {} => t.IsZero() => true
// var t = time.Unix(0, 0) => t.IsZero() => false
func IsValidTime(t time.Time) bool {
	if t.IsZero() {
		return false
	}
	if t.Unix() <= 0 {
		return false
	}
	return true
}

func CallAnyMethod(v interface{}, method string) interface{} {
	ref := reflect.ValueOf(v)
	refKind := ref.Kind()
	if refKind == reflect.Ptr {
		refKind = ref.Elem().Kind()
	}
	// 如果是结构的话，尝试检索一下他是否有Int、ToInt的函数
	// 不再限制为Struct
//	if refKind == reflect.Struct {
	fn := ref.MethodByName(method)
	if fn.IsValid() {
		rs := fn.Call(nil)
		if len(rs) > 0 {
			return rs[0].Interface()
		}
	}
//	}
	return nil
}

// Kind在reflect已经有比较明确的枚举，能比较方便的去进行比较
func KindOf(v interface {}) reflect.Kind {
	ref := reflect.ValueOf(v)
	refKind := ref.Kind()
	if refKind == reflect.Ptr {
		return ref.Elem().Kind()
	}
	return refKind
}

func ValueOf(v interface {}) reflect.Value {
	ref := reflect.ValueOf(v)
	refKind := ref.Kind()
	if refKind == reflect.Ptr {
		return ref.Elem()
	}
	return ref
}

// 转型最好优先转型到最大的值，然后再往底缩进
// 更精确的做法，应该是根据位长，来做出适当的判断但过度优化，又不如直接用go提供一些方法
// 所以这个方法只是确保值的有效性转换，性能在能考虑的条件下，才考虑
func AnyToInt64(v interface{}) int64 {
	switch v.(type) {
		// #1
		// 这个nil必须保持，不然在检索结构的方法时，有可能会陷入死循环
		case nil        : return 0
		// #2
		case bool       : if v == true { return 1 }
		// #3
		// 这玩意可真算不上优雅啊，go怎么就没有泛型呢？
		case int        : if conv, ok := v.(int); ok { return int64(conv) }
		case int8       : if conv, ok := v.(int8); ok { return int64(conv) }
		case int16      : if conv, ok := v.(int16); ok { return int64(conv) }
		case int32      : if conv, ok := v.(int32); ok { return int64(conv) }
		case int64      : if conv, ok := v.(int64); ok { return int64(conv) }
		case uint       : if conv, ok := v.(uint); ok { return int64(conv) }
		case uint8      : if conv, ok := v.(uint8); ok { return int64(conv) }
		case uint16     : if conv, ok := v.(uint16); ok { return int64(conv) }
		case uint32     : if conv, ok := v.(uint32); ok { return int64(conv) }
		case uint64     : if conv, ok := v.(uint64); ok { return int64(conv) } // 这里仍然是有问题
		// #4
		case float32    : if conv, ok := v.(float32); ok { return int64(conv) }
		case float64    : if conv, ok := v.(float64); ok { return int64(conv) }
		// #5
		case string     : if conv, ok := v.(string); ok { return StrToFInt64(conv) }
		// #6
		case time.Time  : if conv, ok := v.(time.Time); ok { return conv.Unix() }
		// #999
		default :
		rs := CallAnyMethod(v, "Int")
		if rs != nil {
			return AnyToInt64(rs)
		} else {
			return KindToInt64(v)
		}
	}
	return 0
}

func AnyToInt(v interface{}) int {
	return int(AnyToInt64(v))
}

// 小位数的整型，还是经常会用到的
// 溢出值，以溢出的最大值处理，而不要作为负数处理
// 256 / 2
func AnyToInt8(v interface{}) int8 {
	iv := AnyToInt64(v)
	if iv > 127 { // 0 - 127
		return int8(127)
	} else if iv < -128 { // -128 - -1
		return int8(-128)
	}
	return int8(iv)
}

// 小位数的整型，还是经常会用到的
// 溢出值，以溢出的最大值处理，而不要作为负数处理
// 65536 / 2
func AnyToInt16(v interface{}) int16 {
	iv := AnyToInt64(v)
	if iv > 32767 { // 0 - 127
		return int16(32767)
	} else if iv < -32768 { // -128 - -1
		return int16(-32768)
	}
	return int16(iv)
}

func AnyToUInt64(v interface{}) uint64 {
	return uint64(AnyToInt64(v))
}

// 无负数的小位数整型，其实也是很常用到的
func AnyToUInt8(v interface{}) uint8 {
	iv := AnyToInt64(v)
	if iv > 255 { // 0 - 127
		return uint8(255)
	} else if iv < 0 { // -128 - -1
		return uint8(0)
	}
	return uint8(iv)
}

// 无负数的小位数整型，其实也是很常用到的，32的就自己手动转吧，uint32能表达的值意境非常大了
func AnyToUInt16(v interface{}) uint16 {
	iv := AnyToInt64(v)
	if iv > 65535 { // 0 - 127
		return uint16(65535)
	} else if iv < 0 { // -128 - -1
		return uint16(0)
	}
	return uint16(iv)
}

func AnyToBool(v interface{}) bool {
	switch v.(type) {
		// #1
		// 这个nil必须保持，不然在检索结构的方法时，有可能会陷入死循环
		case nil        : return false
		// #2
		case bool       : if v == true { return true }
		// #3
		// 这玩意可真算不上优雅啊，go怎么就没有泛型呢？
		case int        : if conv, ok := v.(int); ok { return conv > 0 }
		case int8       : if conv, ok := v.(int8); ok { return conv > 0 }
		case int16      : if conv, ok := v.(int16); ok { return conv > 0 }
		case int32      : if conv, ok := v.(int32); ok { return conv > 0 }
		case int64      : if conv, ok := v.(int64); ok { return conv > 0 }
		case uint       : if conv, ok := v.(uint); ok { return conv > 0 }
		case uint8      : if conv, ok := v.(uint8); ok { return conv > 0 }
		case uint16     : if conv, ok := v.(uint16); ok { return conv > 0 }
		case uint32     : if conv, ok := v.(uint32); ok { return conv > 0 }
		case uint64     : if conv, ok := v.(uint64); ok { return conv > 0 }
		// #4
		case float32    : if conv, ok := v.(float32); ok { return conv > 0 }
		case float64    : if conv, ok := v.(float64); ok { return conv > 0 }
		// #5
		case string     : if conv, ok := v.(string); ok { return len(conv) > 0 }
		// #6
		case time.Time  : if conv, ok := v.(time.Time); ok { return IsValidTime(conv) }
		// #999
		default :
		kind := KindOf(v)
		val := ValueOf(v)
		if kind == reflect.Slice || kind == reflect.Array || kind == reflect.Map {
			return val.Len() > 0
		} else if kind == reflect.Struct {
			// 结构真的没什么好判断的，只要不是无效的结构，就只好返回true了
			if val.IsValid() {
				return true
			}
		}
	}
	return false
}

// 注意，所有其他的AnyTo转换，都不处理[]byte，因为实际上[]byte的情况会比较复杂，他可能包含了encode/gob的编码格式，也可能是json格式
// 也可能用户自己打包的，所以我们不做任何处理
// 但AnyToStr的话还是要处理，尝试最简单的转换
func AnyToStr(v interface{}) string {
	switch v.(type) {
		// #1
		// 这个nil必须保持，不然在检索结构的方法时，有可能会陷入死循环
		case nil        : return ""
		// #2
		// 布尔类型，应该返回个啥呢？真头疼，暂时先返回一个1吧，总比返回了true好
		case bool       : if v == true { return "1" }
		// #3
		// 这玩意可真算不上优雅啊，go怎么就没有泛型呢？
		case int        : if conv, ok := v.(int); ok { return strconv.Itoa(conv) }
		case int8       : if conv, ok := v.(int8); ok { return strconv.Itoa(int(conv)) }
		case int16      : if conv, ok := v.(int16); ok { return strconv.Itoa(int(conv)) }
		case int32      : if conv, ok := v.(int32); ok { return strconv.Itoa(int(conv)) } // 32bit 64bit系统都能涵盖了这个值
		case int64      : if conv, ok := v.(int64); ok { return fmt.Sprint(conv) }
		case uint       : if conv, ok := v.(uint); ok { return fmt.Sprint(conv) }
		case uint8      : if conv, ok := v.(uint8); ok { return strconv.Itoa(int(conv)) }
		case uint16     : if conv, ok := v.(uint16); ok { return strconv.Itoa(int(conv)) }
		case uint32     : if conv, ok := v.(uint32); ok { return fmt.Sprint(conv) } // 32无负数整型，转int就少了一截了
		case uint64     : if conv, ok := v.(uint64); ok { return fmt.Sprint(conv) } // 64位无负数整型，就更加是少了一截了。
		// #4
		case float32    : if conv, ok := v.(float32); ok { return strconv.FormatFloat(float64(conv), 'f', -1, 64) }
		case float64    : if conv, ok := v.(float64); ok { return strconv.FormatFloat(conv, 'f', -1, 64) }
		// #5
		case []byte     : if conv, ok := v.([]byte); ok { return string(conv) }
		case string     : if conv, ok := v.(string); ok { return conv }
		// #6
		case time.Time  : if conv, ok := v.(time.Time); ok { return conv.String() }
		// #999
		default :
		// 数组、切片、Map转类型是什么类型呢？
		return AnyToStr(CallAnyMethod(v, "String"))
	}
	return ""
}

func AnyToFloat(v interface{}) float64 {
	switch v.(type) {
		// #1
		// 这个nil必须保持，不然在检索结构的方法时，有可能会陷入死循环
		case nil        : return 0
		// #2
		case bool       : if v == true { return 1 }
		// #3
		// 这玩意可真算不上优雅啊，go怎么就没有泛型呢？
		case int        : if conv, ok := v.(int); ok { return float64(conv) }
		case int8       : if conv, ok := v.(int8); ok { return float64(conv) }
		case int16      : if conv, ok := v.(int16); ok { return float64(conv) }
		case int32      : if conv, ok := v.(int32); ok { return float64(conv) }
		case int64      : if conv, ok := v.(int64); ok { return float64(conv) }
		case uint       : if conv, ok := v.(uint); ok { return float64(conv) }
		case uint8      : if conv, ok := v.(uint8); ok { return float64(conv) }
		case uint16     : if conv, ok := v.(uint16); ok { return float64(conv) }
		case uint32     : if conv, ok := v.(uint32); ok { return float64(conv) }
		case uint64     : if conv, ok := v.(uint64); ok { return float64(conv) } // 这里仍然是有问题
		// #4
		case float32    : if conv, ok := v.(float32); ok { return float64(conv) }
		case float64    : if conv, ok := v.(float64); ok { return float64(conv) }
		// #5
		case string     : if conv, ok := v.(string); ok { return StrToFloat(conv) }
		// #6
		case time.Time  : if conv, ok := v.(time.Time); ok { return float64(conv.Unix()) }
		// #999
		default :
		// 数组、切片、Map转类型是什么类型呢？
		return AnyToFloat(CallAnyMethod(v, "Float"))
	}
	return 0
}

func AnyToRound(v interface{}, places int) float64 {
	return Round(AnyToFloat(v), places)
}

func AnyToTime(v interface{}) time.Time {
	switch v.(type) {
		// #1
		// 这个nil必须保持，不然在检索结构的方法时，有可能会陷入死循环
		case nil :
		return time.Time{}
		// #2
		// 布尔类型，应该返回个啥呢？真头疼，暂时先返回一个1吧，总比返回了true好
		case bool :
		if v == true {
			return time.Now()
		}
		// #3
		case int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64:
		return time.Unix(AnyToInt64(v), 0)
		// #4
		case float32, float64 :
		return time.Unix(AnyToInt64(v), 0)
		// #5
		case string :
		if conv, ok := v.(string); ok {
			return StrToTime(conv, "Y-m-d H:i:s")
		}
		// #6
		case time.Time :
		// 时间格式，该转成什么字符串呢？
		if conv, ok := v.(time.Time); ok {
			return conv // 先返回默认的字符表达值吧
		}
		// #999
		default :
		// 数组、切片、Map转类型是什么类型呢？
		return AnyToTime(CallAnyMethod(v, "Time"))
	}
	return time.Time{}
}

func KindToInt64(v interface{}) int64 {
	kind := KindOf(v)
	value := ValueOf(v)
	switch kind {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64 :
		return value.Int()
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64 :
		return AnyToInt64(value.Uint())
		case reflect.Float32, reflect.Float64 :
		return AnyToInt64(value.Float())
		case reflect.String :
		return AnyToInt64(value.String())
		case reflect.Bool :
		return AnyToInt64(value.Bool())
		case reflect.Struct :
		if value.Type().String() == "time.Time" {
			if conv, ok := v.(time.Time); ok { return conv.Unix() }
		}
	}
	return 0
}

// 这是网上找到的一种做法，但是还没测试过
//func GetBytes(v interface{}) ([]byte, error) {
//    var buf bytes.Buffer
//    enc := gob.NewEncoder(&buf)
//    err := enc.Encode(v)
//    if err != nil {
//        return nil, err
//    }
//    return buf.Bytes(), nil
//}
//
//func BytesDecode(raw []byte, v interface{}) (interface{}, error) {
//    buf := bytes.NewBuffer(raw)
//    dec := gob.NewDecoder(buf)
//    err := dec.Decode(&v)
//    if err != nil {
//        return nil, err
//    }
//    return v, err
//}
//
//// 这里实现的比较丑陋一点……
//func AnyToBytes(v interface{}) []byte {
//    switch v.(type) {
//        // #1
//        // 这个nil必须保持，不然在检索结构的方法时，有可能会陷入死循环
//        case nil :
//        return nil
//        // #2
//        // 布尔类型，应该返回个啥呢？真头疼，暂时先返回一个1吧，总比返回了true好
//        case bool :
//        if v == true {
//            return []byte {1}
//        } else {
//            return []byte {0}
//        }
//        return nil
//        // #3
//        case int, int32, int64 :
//        if conv, ok := v.(int); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, int64(conv))
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        case int8 :
//        if conv, ok := v.(int8); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        case int16 :
//        if conv, ok := v.(int16); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        case uint8 :
//        if conv, ok := v.(uint8); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        case uint16 :
//        if conv, ok := v.(uint16); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        case uint32 :
//        if conv, ok := v.(uint32); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        case uint64 :
//        if conv, ok := v.(uint32); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        // #4
//        case float32, float64 :
//        if conv, ok := v.(float64); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv)
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        // #5
//        case string :
//        if conv, ok := v.(string); ok {
//            return []byte(conv)
//        }
//        // #6
//        case time.Time :
//        if conv, ok := v.(time.Time); ok {
//            buf := new(bytes.Buffer)
//            err := binary.Write(buf, osEndian, conv.Unix())
//            if err == nil {
//                return buf.Bytes()
//            }
//            return nil
//        }
//        // #7
//        case []byte :
//        if conv, ok := v.([]byte); ok {
//            return conv
//        }
//        // #999
//        default :
//        // 数组、切片、Map怎么转bytes呢？ json encode？
//        return AnyToBytes(CallAnyStructMethod(v, "Bytes"))
//    }
//    return nil
//}